from inspect import isroutine
from typing import (TYPE_CHECKING, Callable, Generic, TypeAlias, TypedDict,
                    TypeVar, overload)
from vengine.ve_constants import MOUSE, VE_KEYMAP

from vengine.ve_coord import Point2d
from vengine.ve_exceptions import VE_EXIT_CODE
from vengine.ve_logger import get_logger

if TYPE_CHECKING:
    from vengine.ve_objects import VE_Clickable, VE_Draggable

T_EventData = TypeVar("T_EventData", bound=TypedDict)
T_EventHandler: TypeAlias = Callable[[type[T_EventData]], None]
T_EventHandlerWrapper: TypeAlias = Callable[[T_EventHandler], T_EventHandler]

class _VE_EventDispatcher(Generic[T_EventData]):
    """Event manager/dispatcher, singleton.
    
    Usage::

        dispatcher = VE_EventDispatcher() # Singleton
        
        @dispatcher.event
        def on_window_resize(self, width, height): ...
        
        @dispatcher.event("on_quit")
        def AnyName(self, exit_code): ...
        
        dispatcher.dispatch("on_quit", 0)
    
    """
    
    def __init__(self):
        self.__handler_map: dict[str, list[Callable[[T_EventData], None]]] = {}
    
    def _add_handler(self, eventId: str, func: T_EventHandler) -> None:
        if self.__handler_map.get(eventId) is None:
            self.__handler_map[eventId] = [func]
            return
        
        self.__handler_map[eventId].append(func)            
    
    @overload
    def event(self) -> T_EventHandlerWrapper: ... # @dispatcher.event()
    @overload
    def event(self, __eventId: str) -> T_EventHandlerWrapper: ... # @dispatcher.event('eventId')
    @overload
    def event(self, __func: T_EventHandler) -> T_EventHandlerWrapper: ... # @dispatcher.event  
    def event(self, *args) -> T_EventHandlerWrapper:
        if len(args) == 0:  # @dispatcher.event()
            def decorator(func: T_EventHandler):
                self._add_handler(func.__name__, func)
                return func
            return decorator
        
        elif isinstance(args[0], str):  # @dispatcher.event('eventId')
            def decorator(func: T_EventHandler):
                self._add_handler(args[0], func)
                return func
            return decorator
        
        elif isroutine(args[0]):  # @dispatcher.event            
            self._add_handler(args[0].__name__, args[0])
            return args[0]
        
        # Error usage
        get_logger().error("Can't find event handler function", stack_info=True)
        exit(VE_EXIT_CODE.EH_NOT_FOUND)
        
    def dispatch(self, eventId: str, eventData: T_EventData | None = None) -> None:
        handlers = self.__handler_map.get(eventId)
        
        if handlers is None: return
        
        e = VE_EventData.none() if eventData is None else eventData
        for handler in handlers:
            handler(e)

VE_EventDispatcher = _VE_EventDispatcher() # Singleton

class VE_EventData:
    @classmethod
    def get_all(cls) -> list[type]:
        types = []
        
        for attrName in dir(cls):
            attr = getattr(cls, attrName)
            
            if isinstance(attr, type) and (attr != cls):
                types.append(attr)
                
        return types
    
    none = TypedDict("none", {})

    # Builtin events
    
    VE_after_init = TypedDict("VE_after_init", {})
    
    VE_quit = TypedDict("VE_quit", {
        "exit_code" : int
    })
    
    VE_display = TypedDict("VE_display", {})
    
    VE_tick = TypedDict("VE_tick", {})
    
    # Keyboard
    
    K_down = TypedDict("K_down", {
        "key": int,
        "mod": int
    })
    
    K_up = TypedDict("K_up", {
        "key": int,
        "mod": int
    })
    
    K_press = TypedDict("K_press", {
        "key" : int,
        "mod" : int
    })
    
    K_release = TypedDict("K_release", {
        "key" : int,
        "mod" : int
    })
    
    # Mouse
    
    M_down = TypedDict("M_down", {
        "pos": tuple[float, float],
        "button": int
    })
    
    M_up = TypedDict("M_up", {
        "pos": tuple[float, float],
        "button": int
    })
    
    M_press = TypedDict("M_press", {
        "pos": tuple[float, float],
        "button": int
    })
    
    M_release = TypedDict("M_release", {
        "pos": tuple[float, float],
        "button": int
    })
    
    M_motion = TypedDict("M_motion", {
        "pos": tuple[float, float],
        "rel" : tuple[float, float],
        "buttons" : tuple[int, int, int]
    })

    # Object events
    
    object_click = TypedDict("object.click", {
        "object" : "VE_Clickable",
        "pos" : Point2d,
        "button" : VE_KEYMAP
    })

    object_drag = TypedDict("object.drag", {
        "object" : "VE_Draggable",
        "pos" : tuple[float, float],
        "rel" : tuple[float, float],
        "buttons" : tuple[int, int, int]
    })